This notebooks explores the results from running cell type annotation with SingleR using the NBAtlas. The NBAtlas reference was aggregated with SingelR prior to model training.

In this notebook, we visualize inferred cell type annotations directly, compare them to normal consensus cell types, and validate cell type assignments with marker genes.

Setup

options(readr.show_col_types = FALSE)
suppressPackageStartupMessages({
  library(ggplot2)
  library(patchwork)
  library(SingleCellExperiment)
})

theme_set(theme_bw())

# Define color ramp for shared use in the heatmaps
heatmap_col_fun <- circlize::colorRamp2(c(0, 1), colors = c("white", "darkslateblue"))
# Set heatmap padding option
ComplexHeatmap::ht_opt(TITLE_PADDING = grid::unit(0.6, "in"))

Paths

# The base path for the OpenScPCA repository, found by its (hidden) .git directory
repository_base <- rprojroot::find_root(rprojroot::is_git_root)

module_dir <- file.path(repository_base, "analyses", "cell-type-neuroblastoma-04")
ref_dir <- file.path(module_dir, "references")
results_dir <- file.path(module_dir, "results", "singler")
data_dir <- file.path(repository_base, "data", "current", "SCPCP000004")
# SingleR files
singler_files <- list.files(
  path = results_dir,
  pattern = "_singler-annotations\\.tsv",
  recursive = TRUE,
  full.names = TRUE
) |>
  # add names as library id
  purrr::set_names(
    \(x) {
      stringr::str_remove(basename(x), "_singler-annotations.tsv")
    }
  )

# merged SCE file
# OBTAINED FROM PORTAL DIRECTLY on 2025-07-09
sce_file <- file.path(
  data_dir,
  "SCPCP000004_merged.rds"
)

# broad consensus cell type groups
validation_url <- "https://raw.githubusercontent.com/AlexsLemonade/OpenScPCA-analysis/refs/heads/main/analyses/cell-type-consensus/references/consensus-validation-groups.tsv"

# palette file
palette_file <- file.path(
  module_dir,
  "palettes",
  "harmonized-cell-type-palette.tsv"
)

# marker genes for validation
consensus_markers_file <- file.path(ref_dir, "consensus-marker-genes.tsv")
nbatlas_markers_file <- file.path(ref_dir, "nbatlas-marker-genes.tsv")

Functions

# Source Jaccard and heatmap utilities functions
source(file.path(module_dir, "scripts", "utils", "jaccard-utils.R"))
#' Function to update NBAtlas labels
#' This function matches NBAtlas labels to consensus labels where appropriate to facilitate plots
#'
#' @param df Data frame with columns to recode
#' @param label_column Name of column with current labels
#' @param recoded_column Name of new column to create with harmonized labels
#'
#' @returns Data frame with new column as specified containing recoded labels
harmonize_celltypes <- function(df, label_column, recoded_column) {
  df |>
    dplyr::mutate(
      {{ recoded_column }} := dplyr::case_when(
        # nbatlas ~ consensus
        {{ label_column }} %in% c("Myeloid", "Fibroblast") ~ stringr::str_to_lower({{ label_column }}),
        {{ label_column }} == "Endothelial" ~ "endothelial cell",
        {{ label_column }} == "Plasma" ~ "plasma cell",
        {{ label_column }} == "NK cell" ~ "natural killer cell",
        # also make the different stromal labels more clearly distinguishable
        {{ label_column }} == "stromal cell" ~ "stromal cell (consensus)",
        {{ label_column }} == "Stromal other" ~ "Stromal other (NBAtlas)",
        .default = {{ label_column }}
      )
    )
}
#' Create a faceted UMAP panel where each panel has only one cell type colored
#' Adapted from scpca-nf:
#' https://github.com/AlexsLemonade/scpca-nf/blob/4bb82aa635b572a62f2028dbec587fcfd2155e26/templates/qc_report/celltypes_qc.rmd#L134-L221
#'
#' @param umap_df Data frame with UMAP1 and UMAP2 columns
#' @param annotation_column Column containing broad cell type annotation
#' @param celltype_colors Named vector of colors to use for each broader validation group
#' @param facet_type Whether to use facet_wrap or facet_grid
#' @param annotation_type_column Additional column to use if facet_type is "grid"
#'
#' @return ggplot object containing a faceted UMAP where each cell type is a facet.
#'   In each panel, the cell type of interest is colored red and all other cells are grey.
faceted_umap <- function(umap_df,
                         annotation_column,
                         celltype_colors,
                         facet_type = c("wrap", "grid"),
                         annotation_type_column = NULL) {
  facet_type <- match.arg(facet_type)

  # color by the annotation column but only color one cell type at a time
  faceted_umap <- ggplot(
    umap_df,
    aes(x = UMAP1, y = UMAP2, color = {{ annotation_column }})
  ) +
    # set points for all "other" points
    geom_point(
      data = dplyr::select(
        umap_df, -{{ annotation_column }}
      ),
      color = "gray90",
      alpha = 0.5,
      size = 0.5
    ) +
    scale_color_manual(values = celltype_colors) +
    # set points for desired cell type
    geom_point(size = 0.5, alpha = 0.5) +
    coord_equal() +
    theme_bw() +
    theme(
      legend.position = "none",
      axis.ticks = element_blank(),
      axis.text = element_blank()
    )

  # add faceting as specified
  if (facet_type == "wrap") {
    faceted_umap <- faceted_umap +
      facet_wrap(
        vars({{ annotation_column }}),
        ncol = 4
      )
  } else {
    faceted_umap <- faceted_umap +
      facet_grid(
        rows = vars({{ annotation_column }}),
        cols = vars({{ annotation_type_column }})
      )
  }
  return(faceted_umap)
}
#' Prepare and create a marker gene expression dotplot
#' Adapted from corresponding code in this ews-nf report:
#' https://github.com/AlexsLemonade/ews-nf/blob/bd38f2bd5ae581ea7dcbd98c5ef717afb9015766/templates/summary-report.Rmd
#'
#' @param merged_sce Merged SCE object to plot with a `cell_id` column in the colData
#' @param markers_df Data frame of marker genes for validation.
#' This function expects columns `marker_gene_label` (cell types), `ensembl_gene_id`, and `gene_symbol`
#' @param singler_df Data frame of singler annotations to plot.
#' This function expects columns called `label_recoded` (cell types) and `cell_id`.
#' The `cell_id` values should match the `cell_id` colData in the merged_sce
#' @param total_cells_df Data frame of cell counts and plot order.
#' This function expects columns called `label_recoded` (cell types), `y_label` (cell types with (total cells) as factor for plot order), and `total_cells`
#' @param expressed_genes Vector of genes that are expressed in the merged_sce
#' @param bar_order Vector for the annotation bar order
#' @param min_cells Only include genes present in at least this many cells
#'
#' @returns Dotplot object
generate_dotplot <- function(
    merged_sce,
    markers_df,
    singler_df,
    total_cells_df,
    expressed_genes,
    bar_order,
    min_cells = 0) {
  all_markers <- markers_df |>
    dplyr::pull(ensembl_gene_id) |>
    unique()

  # consider only markers that are expressed
  expressed_markers <- intersect(all_markers, expressed_genes)

  # get logcounts from merged_sce for expressed genes
  gene_exp_df <- scuttle::makePerCellDF(
    merged_sce,
    features = expressed_markers,
    assay.type = "logcounts",
    use.coldata = "cell_id",
    use.dimred = FALSE
  ) |>
    tidyr::pivot_longer(starts_with("ENSG"), names_to = "ensembl_gene_id", values_to = "logcounts") |>
    dplyr::mutate(detected = logcounts > 0)

  # Join with cell type results and marker gene info
  all_info_df <- singler_df |>
    dplyr::left_join(gene_exp_df, by = "cell_id") |>
    # account for the same gene being present in multiple cell types
    dplyr::left_join(markers_df, by = "ensembl_gene_id", relationship = "many-to-many")

  # define y- and x- axis orders

  # y axis - singler
  y_label_order <- rev(total_cells_df$y_label)

  # x axis - marker genes. this uses the passed in bar_order
  markers_df <- markers_df |>
    dplyr::filter(ensembl_gene_id %in% expressed_markers) |>
    dplyr::mutate(marker_gene_label = factor(marker_gene_label, levels = bar_order)) |>
    dplyr::arrange(marker_gene_label)

  marker_gene_order <- markers_df |>
    dplyr::pull(gene_symbol) |>
    unique()

  group_stats_df <- all_info_df |>
    # remove genes that aren't present in final annotations
    dplyr::filter(gene_symbol %in% marker_gene_order) |>
    # for each assigned cell type/marker gene combo get total detected and mean expression
    dplyr::group_by(label_recoded, marker_gene_label, ensembl_gene_id) |>
    dplyr::summarize(
      detected_count = sum(detected),
      mean_exp = mean(logcounts)
    ) |>
    dplyr::ungroup() |>
    # add total cells
    dplyr::left_join(total_cells_df, by = "label_recoded") |>
    # get total percent expressed
    dplyr::mutate(percent_exp = (detected_count / total_cells) * 100) |>
    # add in validation group for marker genes
    # this includes all possible marker genes and all possible validation group assignments
    dplyr::left_join(
      markers_df,
      by = c("ensembl_gene_id", "marker_gene_label"),
      relationship = "many-to-many"
    )

  dotplot_df <- group_stats_df |>
    dplyr::filter(
      # remove lowly expressed marker genes
      mean_exp > 0,
      percent_exp > 10,
      # only show cells that have above the minimum total number
      total_cells > min_cells
    ) |>
    # set orders of gene symbol and validation groups
    dplyr::mutate(
      y_label = factor(y_label, y_label_order),
      gene_symbol = factor(gene_symbol, levels = marker_gene_order),
      marker_gene_label = factor(marker_gene_label, levels = bar_order)
    )


  ### Make the dotplot!
  dotplot <- ggplot(dotplot_df) +
    aes(
      y = y_label,
      x = gene_symbol,
      color = mean_exp,
      size = percent_exp
    ) +
    geom_point() +
    scale_color_viridis_c(option = "magma") +
    facet_grid(cols = vars(marker_gene_label), scales = "free", space = "free") +
    theme_classic() +
    theme(
      strip.background = element_rect(fill = "transparent", color = NA),
      strip.placement = "outside",
      strip.text.x = element_blank(),
      axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5),
      axis.ticks.x = element_blank(),
      text = element_text(size = 14),
      panel.spacing = unit(0.5, "lines") # adjust spacing and match with annotation bar
    ) +
    labs(
      x = "Validation marker genes",
      y = "SingleR label",
      color = "Mean gene expression",
      size = "Percent cells expressed"
    )


  # add annotation bar aligning marker genes with validation group
  color_bar <- ggplot(dotplot_df, aes(x = gene_symbol, y = 1, fill = marker_gene_label)) +
    geom_tile() +
    facet_grid(cols = vars(marker_gene_label), scales = "free", space = "free") +
    scale_fill_manual(values = celltype_pal, breaks = levels(dotplot_df$marker_gene_label)) +
    ggmap::theme_nothing() +
    theme(
      strip.background = element_rect(fill = "transparent", color = NA),
      strip.text.x = element_text(angle = 90, hjust = 0, vjust = 0.5, size = 12),
      strip.placement = "outside",
      legend.position = "none",
      panel.spacing = unit(0.5, "lines"),
      strip.clip = "off"
    ) +
    labs(fill = "")

  combined_plot <- color_bar / dotplot +
    patchwork::plot_layout(ncol = 1, heights = c(0.1, 4))

  return(combined_plot)
}

Prepare input data

Read SCE object to get consensus cell types and UMAP coordinates:

merged_sce <- readRDS(sce_file)
# immediately remove assays we don't need for space
assay(merged_sce, "spliced") <- NULL
assay(merged_sce, "counts") <- NULL

# RUH ROH!!!
# https://github.com/AlexsLemonade/scpcaTools/issues/298
merged_sce$cell_id <- colnames(merged_sce)

Read cell type data frames:

singler_df <- singler_files |>
  purrr::map(readr::read_tsv) |>
  purrr::list_rbind(names_to = "library_id") |>
  dplyr::select(-delta.next, -labels) |>
  dplyr::mutate(
    # add cell id column for unique rows & joining
    cell_id = glue::glue("{library_id}-{barcodes}"),
    # recode NA -> "unknown_singler"
    pruned.labels = ifelse(
      is.na(pruned.labels),
      "unknown_singler",
      pruned.labels
    )
  )

# validation data frames
validation_df <- readr::read_tsv(validation_url) |>
  dplyr::select(consensus_annotation, validation_group_annotation)
consensus_markers_df <- readr::read_tsv(consensus_markers_file)
nbatlas_markers_df <- readr::read_tsv(nbatlas_markers_file)


# set up palette for umaps
palette_df <- readr::read_tsv(palette_file)
celltype_pal <- palette_df$hex_color
names(celltype_pal) <- palette_df$harmonized_label

Join and prepare data for use:

celltype_df <- scuttle::makePerCellDF(
  merged_sce,
  use.coldata = c("barcodes", "sample_id", "library_id", "consensus_celltype_annotation"),
  use.dimred = c("UMAP")
) |>
  # there are repeated barcodes so we need to keep cell_id around
  tibble::rownames_to_column("cell_id") |>
  dplyr::rename(
    UMAP1 = UMAP.1,
    UMAP2 = UMAP.2,
    consensus_annotation = consensus_celltype_annotation
  ) |>
  dplyr::left_join(validation_df, by = "consensus_annotation") |>
  # recode NAs to "unknown_consensus" and remove full consensus labels
  dplyr::mutate(validation_group_annotation = ifelse(
    is.na(validation_group_annotation),
    "unknown_consensus",
    validation_group_annotation
  )) |>
  dplyr::select(-consensus_annotation) |>
  dplyr::left_join(singler_df, by = c("cell_id", "barcodes", "library_id"))

# Recode NBAtlas cell types where possible so they match with colors
celltype_recoded_df <- celltype_df |>
  # rename to make annotation sources more clear
  dplyr::rename(
    "consensus_validation" = validation_group_annotation,
    "singler" = pruned.labels
  ) |>
  # pivot longer for wrangling
  tidyr::pivot_longer(
    c(consensus_validation, singler),
    names_to = "annotation_type",
    values_to = "label"
  ) |>
  harmonize_celltypes(label, label_recoded) |>
  dplyr::select(-label)

Heatmap

This section compares SingleR annotations to consensus cell type annotations using a heatmap. The heatmap is colored by Jaccard similarity index.

# Pivot wider for heatmap functions
celltype_recoded_wide_df <- celltype_recoded_df |>
  tidyr::pivot_wider(
    names_from = annotation_type,
    values_from = label_recoded
  )

heatmap_col_fun <- circlize::colorRamp2(c(0, 1), colors = c("white", "darkslateblue"))

make_jaccard_heatmap(
  celltype_recoded_wide_df,
  "consensus_validation",
  "singler",
  "Consensus validation label",
  "SingleR NBAtlas label"
)

UMAPs

This section visualizes and compares SingleR annotations to consensus cell type annotations using UMAPs. Note that the displayed UMAP is from a merged object that has not been batch-corrected.

Cell type labels have been harmonized between sources wherever possible. Note that each set of labels has its own “stromal” category which the labels distinguish. In addition, cells labeled unknown_consensus are those with no assigned consensus label, and cells labeled unknown_singler are those where SingleR could not confidently assign a label.

Complete UMAP

First, we display the consensus and SingleR annotations for all cells.

ggplot(celltype_recoded_df) +
  aes(x = UMAP1, y = UMAP2, color = label_recoded) +
  geom_point(size = 0.25, alpha = 0.5) +
  scale_color_manual(
    values = celltype_pal,
    name = "Harmonized cell types"
  ) +
  facet_wrap(vars(annotation_type)) +
  coord_equal() +
  theme(
    legend.position = "bottom",
    axis.ticks = element_blank(),
    axis.text = element_blank()
  ) +
  guides(color = guide_legend(override.aes = list(size = 2, alpha = 1)))

SingleR annotations only

Below we display a faceted UMAP to highlight the SingleR annotations. Light gray cells in each panel represent all other cell types.

celltype_recoded_df |>
  dplyr::filter(annotation_type == "singler") |>
  faceted_umap(
    annotation_column = label_recoded,
    celltype_colors = celltype_pal
  )

Faceted comparison to consensus cell types

Below, we display faceted UMAPs highlighting a single cell type but considering only cell types that have direct correspondence between SingleR and consensus cell types. This allows us to see if the normal cells that SingleR is labeling correspond well to the normal cells identified by consensus labels. Each row displays a cell type where the left column shows the consensus version and the right column shows the SingleR version.

In addition, we include a category below putative-tumor to directly compare the unknown consensus labels with the Neuroendocrine cells labeled by SingleR. While these categories are not necessarily directly comparable, they are each most likely to contain tumor cells.

direct_celltype_matches <- c(
  "B cell",
  "T cell",
  "myeloid",
  "fibroblast",
  "endothelial cell",
  "plasma cell",
  "natural killer cell",
  # we'll use this label to be able to directly compare unknown_consensus to Neuroendocrine
  "putative-tumor"
)


celltype_facet_df <- celltype_recoded_df |>
  # recode so Neuroendocrine matches with unknown_consensus in the plot
  dplyr::mutate(
    label_recoded = ifelse(
      label_recoded %in% c("Neuroendocrine", "unknown_consensus"),
      "putative-tumor",
      label_recoded
    )
  ) |>
  dplyr::filter(label_recoded %in% direct_celltype_matches)

faceted_umap(
  celltype_facet_df,
  annotation_column = label_recoded,
  celltype_colors = celltype_pal,
  facet_type = "grid",
  annotation_type_column = annotation_type
)

Marker gene expression dotplots

In this section we’ll validate annotations using two sets of marker genes:

# Prepare the expressed_genes vector
# we only care about if that gene is expressed otherwise we won't waste memory and include it
gene_sums <- rowData(merged_sce) |>
  as.data.frame() |>
  dplyr::select(contains("detected")) |>
  as.matrix() |>
  rowSums()
expressed_genes <- names(gene_sums)[gene_sums > 0]

# Prepare data frame with singler labels to plot
singler_recoded_df <- celltype_recoded_df |>
  dplyr::filter(
    annotation_type == "singler",
    # we don't consider the NAs here
    label_recoded != "unknown_singler"
  ) |>
  dplyr::select(cell_id, label_recoded)


# get total number of cells per final annotation group and set up y_label
total_cells_df <- singler_recoded_df |>
  dplyr::count(label_recoded, name = "total_cells") |>
  dplyr::arrange(desc(total_cells)) |>
  dplyr::mutate(y_label = glue::glue("{label_recoded} ({total_cells})"))

singler_order <- total_cells_df$y_label
total_cells_df$y_label <- factor(total_cells_df$y_label, levels = singler_order)

NBAtlas marker genes

We’ll show the top-seven highest logFC marker genes per cell type for validation.

# number of marker genes to consider per validation group
n_marker_genes <- 7

nbatlas_markers_df <- nbatlas_markers_df |>
  # keep only the top `n_marker_genes`
  dplyr::filter(direction == "up") |>
  dplyr::group_by(NBAtlas_label) |>
  dplyr::mutate(rank_lfc = rank(-avg_log2FC)) |>
  dplyr::ungroup() |>
  dplyr::filter(rank_lfc <= n_marker_genes) |>
  harmonize_celltypes(NBAtlas_label, marker_gene_label) |>
  dplyr::select(marker_gene_label, gene_symbol, ensembl_gene_id)

nbatlas_bar_order <- total_cells_df$label_recoded
generate_dotplot(
  merged_sce,
  nbatlas_markers_df,
  singler_recoded_df,
  total_cells_df,
  expressed_genes,
  nbatlas_bar_order
)
`summarise()` has grouped output by 'label_recoded', 'marker_gene_label'. You
can override using the `.groups` argument.

Consensus validation marker genes

# prepare for dotplot

consensus_markers_df <- consensus_markers_df |>
  # we'll use this to help determine the bar order
  harmonize_celltypes(NBAtlas_label, NBAtlas_label_recoded) |>
  dplyr::select(
    marker_gene_label = validation_group_annotation,
    ensembl_gene_id,
    gene_symbol,
    NBAtlas_label_recoded
  )

# get the bar order
consensus_bar_order <- total_cells_df |>
  dplyr::select(label_recoded, y_label) |>
  # inner!!!!
  dplyr::inner_join(consensus_markers_df, by = c("label_recoded" = "NBAtlas_label_recoded")) |>
  dplyr::arrange(y_label) |>
  dplyr::pull(marker_gene_label) |>
  unique()
generate_dotplot(
  merged_sce,
  consensus_markers_df,
  singler_recoded_df,
  total_cells_df,
  expressed_genes,
  consensus_bar_order
)
`summarise()` has grouped output by 'label_recoded', 'marker_gene_label'. You
can override using the `.groups` argument.

Session Info

# record the versions of the packages used in this analysis and other environment information
sessionInfo()
R version 4.4.0 (2024-04-24)
Platform: aarch64-apple-darwin20
Running under: macOS 15.5

Matrix products: default
BLAS:   /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRblas.0.dylib 
LAPACK: /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.12.0

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

time zone: America/New_York
tzcode source: internal

attached base packages:
[1] stats4    stats     graphics  grDevices datasets  utils     methods  
[8] base     

other attached packages:
 [1] SingleCellExperiment_1.26.0 SummarizedExperiment_1.34.0
 [3] Biobase_2.64.0              GenomicRanges_1.56.2       
 [5] GenomeInfoDb_1.40.1         IRanges_2.38.1             
 [7] S4Vectors_0.42.1            BiocGenerics_0.50.0        
 [9] MatrixGenerics_1.16.0       matrixStats_1.5.0          
[11] patchwork_1.3.0             ggplot2_3.5.2              

loaded via a namespace (and not attached):
 [1] tidyselect_1.2.1          viridisLite_0.4.2        
 [3] dplyr_1.1.4               farver_2.1.2             
 [5] bitops_1.0-9              fastmap_1.2.0            
 [7] digest_0.6.37             lifecycle_1.0.4          
 [9] cluster_2.1.8             Cairo_1.6-2              
[11] magrittr_2.0.3            compiler_4.4.0           
[13] rlang_1.1.6               sass_0.4.10              
[15] tools_4.4.0               yaml_2.3.10              
[17] knitr_1.50                labeling_0.4.3           
[19] S4Arrays_1.4.1            bit_4.6.0                
[21] curl_6.3.0                DelayedArray_0.30.1      
[23] plyr_1.8.9                RColorBrewer_1.1-3       
[25] abind_1.4-8               BiocParallel_1.38.0      
[27] withr_3.0.2               purrr_1.0.4              
[29] grid_4.4.0                beachmat_2.20.0          
[31] colorspace_2.1-1          scales_1.4.0             
[33] iterators_1.0.14          cli_3.6.5                
[35] rmarkdown_2.29            crayon_1.5.3             
[37] generics_0.1.4            httr_1.4.7               
[39] tzdb_0.5.0                rjson_0.2.23             
[41] DelayedMatrixStats_1.26.0 scuttle_1.14.0           
[43] cachem_1.1.0              stringr_1.5.1            
[45] zlibbioc_1.50.0           parallel_4.4.0           
[47] BiocManager_1.30.26       XVector_0.44.0           
[49] vctrs_0.6.5               Matrix_1.7-1             
[51] jsonlite_2.0.0            GetoptLong_1.0.5         
[53] hms_1.1.3                 bit64_4.6.0-1            
[55] clue_0.3-66               jpeg_0.1-11              
[57] foreach_1.5.2             tidyr_1.3.1              
[59] jquerylib_0.1.4           glue_1.8.0               
[61] codetools_0.2-20          stringi_1.8.7            
[63] shape_1.4.6.1             gtable_0.3.6             
[65] UCSC.utils_1.0.0          ComplexHeatmap_2.20.0    
[67] tibble_3.3.0              pillar_1.10.2            
[69] htmltools_0.5.8.1         GenomeInfoDbData_1.2.12  
[71] circlize_0.4.16           R6_2.6.1                 
[73] sparseMatrixStats_1.16.0  doParallel_1.0.17        
[75] rprojroot_2.0.4           vroom_1.6.5              
[77] evaluate_1.0.3            lattice_0.22-6           
[79] readr_2.1.5               png_0.1-8                
[81] renv_1.1.3                bslib_0.9.0              
[83] ggmap_4.0.1               Rcpp_1.0.14              
[85] SparseArray_1.4.8         xfun_0.52                
[87] pkgconfig_2.0.3           GlobalOptions_0.1.2      
LS0tCnRpdGxlOiAiRXhwbG9yZSB0aGUgU2luZ2xlUiByZXN1bHRzIgphdXRob3I6ICJTdGVwaGFuaWUgSi4gU3BpZWxtYW4iCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMwogICAgY29kZV9mb2xkaW5nOiBoaWRlCi0tLQoKVGhpcyBub3RlYm9va3MgZXhwbG9yZXMgdGhlIHJlc3VsdHMgZnJvbSBydW5uaW5nIGNlbGwgdHlwZSBhbm5vdGF0aW9uIHdpdGggYFNpbmdsZVJgIHVzaW5nIHRoZSBOQkF0bGFzLgpUaGUgTkJBdGxhcyByZWZlcmVuY2Ugd2FzIGFnZ3JlZ2F0ZWQgd2l0aCBgU2luZ2VsUmAgcHJpb3IgdG8gbW9kZWwgdHJhaW5pbmcuCgpJbiB0aGlzIG5vdGVib29rLCB3ZSB2aXN1YWxpemUgaW5mZXJyZWQgY2VsbCB0eXBlIGFubm90YXRpb25zIGRpcmVjdGx5LCBjb21wYXJlIHRoZW0gdG8gbm9ybWFsIGNvbnNlbnN1cyBjZWxsIHR5cGVzLCBhbmQgdmFsaWRhdGUgY2VsbCB0eXBlIGFzc2lnbm1lbnRzIHdpdGggbWFya2VyIGdlbmVzLgoKCiMjIFNldHVwCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFfQpvcHRpb25zKHJlYWRyLnNob3dfY29sX3R5cGVzID0gRkFMU0UpCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7CiAgbGlicmFyeShnZ3Bsb3QyKQogIGxpYnJhcnkocGF0Y2h3b3JrKQogIGxpYnJhcnkoU2luZ2xlQ2VsbEV4cGVyaW1lbnQpCn0pCgp0aGVtZV9zZXQodGhlbWVfYncoKSkKCiMgRGVmaW5lIGNvbG9yIHJhbXAgZm9yIHNoYXJlZCB1c2UgaW4gdGhlIGhlYXRtYXBzCmhlYXRtYXBfY29sX2Z1biA8LSBjaXJjbGl6ZTo6Y29sb3JSYW1wMihjKDAsIDEpLCBjb2xvcnMgPSBjKCJ3aGl0ZSIsICJkYXJrc2xhdGVibHVlIikpCiMgU2V0IGhlYXRtYXAgcGFkZGluZyBvcHRpb24KQ29tcGxleEhlYXRtYXA6Omh0X29wdChUSVRMRV9QQURESU5HID0gZ3JpZDo6dW5pdCgwLjYsICJpbiIpKQpgYGAKCgojIyMgUGF0aHMKCmBgYHtyIGJhc2UgcGF0aHN9CiMgVGhlIGJhc2UgcGF0aCBmb3IgdGhlIE9wZW5TY1BDQSByZXBvc2l0b3J5LCBmb3VuZCBieSBpdHMgKGhpZGRlbikgLmdpdCBkaXJlY3RvcnkKcmVwb3NpdG9yeV9iYXNlIDwtIHJwcm9qcm9vdDo6ZmluZF9yb290KHJwcm9qcm9vdDo6aXNfZ2l0X3Jvb3QpCgptb2R1bGVfZGlyIDwtIGZpbGUucGF0aChyZXBvc2l0b3J5X2Jhc2UsICJhbmFseXNlcyIsICJjZWxsLXR5cGUtbmV1cm9ibGFzdG9tYS0wNCIpCnJlZl9kaXIgPC0gZmlsZS5wYXRoKG1vZHVsZV9kaXIsICJyZWZlcmVuY2VzIikKcmVzdWx0c19kaXIgPC0gZmlsZS5wYXRoKG1vZHVsZV9kaXIsICJyZXN1bHRzIiwgInNpbmdsZXIiKQpkYXRhX2RpciA8LSBmaWxlLnBhdGgocmVwb3NpdG9yeV9iYXNlLCAiZGF0YSIsICJjdXJyZW50IiwgIlNDUENQMDAwMDA0IikKYGBgCgoKYGBge3IgZmlsZSBwYXRoc30KIyBTaW5nbGVSIGZpbGVzCnNpbmdsZXJfZmlsZXMgPC0gbGlzdC5maWxlcygKICBwYXRoID0gcmVzdWx0c19kaXIsCiAgcGF0dGVybiA9ICJfc2luZ2xlci1hbm5vdGF0aW9uc1xcLnRzdiIsCiAgcmVjdXJzaXZlID0gVFJVRSwKICBmdWxsLm5hbWVzID0gVFJVRQopIHw+CiAgIyBhZGQgbmFtZXMgYXMgbGlicmFyeSBpZAogIHB1cnJyOjpzZXRfbmFtZXMoCiAgICBcKHgpIHsKICAgICAgc3RyaW5ncjo6c3RyX3JlbW92ZShiYXNlbmFtZSh4KSwgIl9zaW5nbGVyLWFubm90YXRpb25zLnRzdiIpCiAgICB9CiAgKQoKIyBtZXJnZWQgU0NFIGZpbGUKIyBPQlRBSU5FRCBGUk9NIFBPUlRBTCBESVJFQ1RMWSBvbiAyMDI1LTA3LTA5CnNjZV9maWxlIDwtIGZpbGUucGF0aCgKICBkYXRhX2RpciwKICAiU0NQQ1AwMDAwMDRfbWVyZ2VkLnJkcyIKKQoKIyBicm9hZCBjb25zZW5zdXMgY2VsbCB0eXBlIGdyb3Vwcwp2YWxpZGF0aW9uX3VybCA8LSAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0FsZXhzTGVtb25hZGUvT3BlblNjUENBLWFuYWx5c2lzL3JlZnMvaGVhZHMvbWFpbi9hbmFseXNlcy9jZWxsLXR5cGUtY29uc2Vuc3VzL3JlZmVyZW5jZXMvY29uc2Vuc3VzLXZhbGlkYXRpb24tZ3JvdXBzLnRzdiIKCiMgcGFsZXR0ZSBmaWxlCnBhbGV0dGVfZmlsZSA8LSBmaWxlLnBhdGgoCiAgbW9kdWxlX2RpciwKICAicGFsZXR0ZXMiLAogICJoYXJtb25pemVkLWNlbGwtdHlwZS1wYWxldHRlLnRzdiIKKQoKIyBtYXJrZXIgZ2VuZXMgZm9yIHZhbGlkYXRpb24KY29uc2Vuc3VzX21hcmtlcnNfZmlsZSA8LSBmaWxlLnBhdGgocmVmX2RpciwgImNvbnNlbnN1cy1tYXJrZXItZ2VuZXMudHN2IikKbmJhdGxhc19tYXJrZXJzX2ZpbGUgPC0gZmlsZS5wYXRoKHJlZl9kaXIsICJuYmF0bGFzLW1hcmtlci1nZW5lcy50c3YiKQpgYGAKCiMjIyBGdW5jdGlvbnMKCmBgYHtyfQojIFNvdXJjZSBKYWNjYXJkIGFuZCBoZWF0bWFwIHV0aWxpdGllcyBmdW5jdGlvbnMKc291cmNlKGZpbGUucGF0aChtb2R1bGVfZGlyLCAic2NyaXB0cyIsICJ1dGlscyIsICJqYWNjYXJkLXV0aWxzLlIiKSkKYGBgCgpgYGB7cn0KIycgRnVuY3Rpb24gdG8gdXBkYXRlIE5CQXRsYXMgbGFiZWxzCiMnIFRoaXMgZnVuY3Rpb24gbWF0Y2hlcyBOQkF0bGFzIGxhYmVscyB0byBjb25zZW5zdXMgbGFiZWxzIHdoZXJlIGFwcHJvcHJpYXRlIHRvIGZhY2lsaXRhdGUgcGxvdHMKIycKIycgQHBhcmFtIGRmIERhdGEgZnJhbWUgd2l0aCBjb2x1bW5zIHRvIHJlY29kZQojJyBAcGFyYW0gbGFiZWxfY29sdW1uIE5hbWUgb2YgY29sdW1uIHdpdGggY3VycmVudCBsYWJlbHMKIycgQHBhcmFtIHJlY29kZWRfY29sdW1uIE5hbWUgb2YgbmV3IGNvbHVtbiB0byBjcmVhdGUgd2l0aCBoYXJtb25pemVkIGxhYmVscwojJwojJyBAcmV0dXJucyBEYXRhIGZyYW1lIHdpdGggbmV3IGNvbHVtbiBhcyBzcGVjaWZpZWQgY29udGFpbmluZyByZWNvZGVkIGxhYmVscwpoYXJtb25pemVfY2VsbHR5cGVzIDwtIGZ1bmN0aW9uKGRmLCBsYWJlbF9jb2x1bW4sIHJlY29kZWRfY29sdW1uKSB7CiAgZGYgfD4KICAgIGRwbHlyOjptdXRhdGUoCiAgICAgIHt7IHJlY29kZWRfY29sdW1uIH19IDo9IGRwbHlyOjpjYXNlX3doZW4oCiAgICAgICAgIyBuYmF0bGFzIH4gY29uc2Vuc3VzCiAgICAgICAge3sgbGFiZWxfY29sdW1uIH19ICVpbiUgYygiTXllbG9pZCIsICJGaWJyb2JsYXN0IikgfiBzdHJpbmdyOjpzdHJfdG9fbG93ZXIoe3sgbGFiZWxfY29sdW1uIH19KSwKICAgICAgICB7eyBsYWJlbF9jb2x1bW4gfX0gPT0gIkVuZG90aGVsaWFsIiB+ICJlbmRvdGhlbGlhbCBjZWxsIiwKICAgICAgICB7eyBsYWJlbF9jb2x1bW4gfX0gPT0gIlBsYXNtYSIgfiAicGxhc21hIGNlbGwiLAogICAgICAgIHt7IGxhYmVsX2NvbHVtbiB9fSA9PSAiTksgY2VsbCIgfiAibmF0dXJhbCBraWxsZXIgY2VsbCIsCiAgICAgICAgIyBhbHNvIG1ha2UgdGhlIGRpZmZlcmVudCBzdHJvbWFsIGxhYmVscyBtb3JlIGNsZWFybHkgZGlzdGluZ3Vpc2hhYmxlCiAgICAgICAge3sgbGFiZWxfY29sdW1uIH19ID09ICJzdHJvbWFsIGNlbGwiIH4gInN0cm9tYWwgY2VsbCAoY29uc2Vuc3VzKSIsCiAgICAgICAge3sgbGFiZWxfY29sdW1uIH19ID09ICJTdHJvbWFsIG90aGVyIiB+ICJTdHJvbWFsIG90aGVyIChOQkF0bGFzKSIsCiAgICAgICAgLmRlZmF1bHQgPSB7eyBsYWJlbF9jb2x1bW4gfX0KICAgICAgKQogICAgKQp9CmBgYAoKCmBgYHtyfQojJyBDcmVhdGUgYSBmYWNldGVkIFVNQVAgcGFuZWwgd2hlcmUgZWFjaCBwYW5lbCBoYXMgb25seSBvbmUgY2VsbCB0eXBlIGNvbG9yZWQKIycgQWRhcHRlZCBmcm9tIHNjcGNhLW5mOgojJyBodHRwczovL2dpdGh1Yi5jb20vQWxleHNMZW1vbmFkZS9zY3BjYS1uZi9ibG9iLzRiYjgyYWE2MzViNTcyYTYyZjIwMjhkYmVjNTg3ZmNmZDIxNTVlMjYvdGVtcGxhdGVzL3FjX3JlcG9ydC9jZWxsdHlwZXNfcWMucm1kI0wxMzQtTDIyMQojJwojJyBAcGFyYW0gdW1hcF9kZiBEYXRhIGZyYW1lIHdpdGggVU1BUDEgYW5kIFVNQVAyIGNvbHVtbnMKIycgQHBhcmFtIGFubm90YXRpb25fY29sdW1uIENvbHVtbiBjb250YWluaW5nIGJyb2FkIGNlbGwgdHlwZSBhbm5vdGF0aW9uCiMnIEBwYXJhbSBjZWxsdHlwZV9jb2xvcnMgTmFtZWQgdmVjdG9yIG9mIGNvbG9ycyB0byB1c2UgZm9yIGVhY2ggYnJvYWRlciB2YWxpZGF0aW9uIGdyb3VwCiMnIEBwYXJhbSBmYWNldF90eXBlIFdoZXRoZXIgdG8gdXNlIGZhY2V0X3dyYXAgb3IgZmFjZXRfZ3JpZAojJyBAcGFyYW0gYW5ub3RhdGlvbl90eXBlX2NvbHVtbiBBZGRpdGlvbmFsIGNvbHVtbiB0byB1c2UgaWYgZmFjZXRfdHlwZSBpcyAiZ3JpZCIKIycKIycgQHJldHVybiBnZ3Bsb3Qgb2JqZWN0IGNvbnRhaW5pbmcgYSBmYWNldGVkIFVNQVAgd2hlcmUgZWFjaCBjZWxsIHR5cGUgaXMgYSBmYWNldC4KIycgICBJbiBlYWNoIHBhbmVsLCB0aGUgY2VsbCB0eXBlIG9mIGludGVyZXN0IGlzIGNvbG9yZWQgcmVkIGFuZCBhbGwgb3RoZXIgY2VsbHMgYXJlIGdyZXkuCmZhY2V0ZWRfdW1hcCA8LSBmdW5jdGlvbih1bWFwX2RmLAogICAgICAgICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9jb2x1bW4sCiAgICAgICAgICAgICAgICAgICAgICAgICBjZWxsdHlwZV9jb2xvcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICBmYWNldF90eXBlID0gYygid3JhcCIsICJncmlkIiksCiAgICAgICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX3R5cGVfY29sdW1uID0gTlVMTCkgewogIGZhY2V0X3R5cGUgPC0gbWF0Y2guYXJnKGZhY2V0X3R5cGUpCgogICMgY29sb3IgYnkgdGhlIGFubm90YXRpb24gY29sdW1uIGJ1dCBvbmx5IGNvbG9yIG9uZSBjZWxsIHR5cGUgYXQgYSB0aW1lCiAgZmFjZXRlZF91bWFwIDwtIGdncGxvdCgKICAgIHVtYXBfZGYsCiAgICBhZXMoeCA9IFVNQVAxLCB5ID0gVU1BUDIsIGNvbG9yID0ge3sgYW5ub3RhdGlvbl9jb2x1bW4gfX0pCiAgKSArCiAgICAjIHNldCBwb2ludHMgZm9yIGFsbCAib3RoZXIiIHBvaW50cwogICAgZ2VvbV9wb2ludCgKICAgICAgZGF0YSA9IGRwbHlyOjpzZWxlY3QoCiAgICAgICAgdW1hcF9kZiwgLXt7IGFubm90YXRpb25fY29sdW1uIH19CiAgICAgICksCiAgICAgIGNvbG9yID0gImdyYXk5MCIsCiAgICAgIGFscGhhID0gMC41LAogICAgICBzaXplID0gMC41CiAgICApICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjZWxsdHlwZV9jb2xvcnMpICsKICAgICMgc2V0IHBvaW50cyBmb3IgZGVzaXJlZCBjZWxsIHR5cGUKICAgIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSwgYWxwaGEgPSAwLjUpICsKICAgIGNvb3JkX2VxdWFsKCkgKwogICAgdGhlbWVfYncoKSArCiAgICB0aGVtZSgKICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLAogICAgICBheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCkKICAgICkKCiAgIyBhZGQgZmFjZXRpbmcgYXMgc3BlY2lmaWVkCiAgaWYgKGZhY2V0X3R5cGUgPT0gIndyYXAiKSB7CiAgICBmYWNldGVkX3VtYXAgPC0gZmFjZXRlZF91bWFwICsKICAgICAgZmFjZXRfd3JhcCgKICAgICAgICB2YXJzKHt7IGFubm90YXRpb25fY29sdW1uIH19KSwKICAgICAgICBuY29sID0gNAogICAgICApCiAgfSBlbHNlIHsKICAgIGZhY2V0ZWRfdW1hcCA8LSBmYWNldGVkX3VtYXAgKwogICAgICBmYWNldF9ncmlkKAogICAgICAgIHJvd3MgPSB2YXJzKHt7IGFubm90YXRpb25fY29sdW1uIH19KSwKICAgICAgICBjb2xzID0gdmFycyh7eyBhbm5vdGF0aW9uX3R5cGVfY29sdW1uIH19KQogICAgICApCiAgfQogIHJldHVybihmYWNldGVkX3VtYXApCn0KYGBgCgoKCmBgYHtyfQojJyBQcmVwYXJlIGFuZCBjcmVhdGUgYSBtYXJrZXIgZ2VuZSBleHByZXNzaW9uIGRvdHBsb3QKIycgQWRhcHRlZCBmcm9tIGNvcnJlc3BvbmRpbmcgY29kZSBpbiB0aGlzIGV3cy1uZiByZXBvcnQ6CiMnIGh0dHBzOi8vZ2l0aHViLmNvbS9BbGV4c0xlbW9uYWRlL2V3cy1uZi9ibG9iL2JkMzhmMmJkNWFlNTgxZWE3ZGNiZDk4YzVlZjcxN2FmYjkwMTU3NjYvdGVtcGxhdGVzL3N1bW1hcnktcmVwb3J0LlJtZAojJwojJyBAcGFyYW0gbWVyZ2VkX3NjZSBNZXJnZWQgU0NFIG9iamVjdCB0byBwbG90IHdpdGggYSBgY2VsbF9pZGAgY29sdW1uIGluIHRoZSBjb2xEYXRhCiMnIEBwYXJhbSBtYXJrZXJzX2RmIERhdGEgZnJhbWUgb2YgbWFya2VyIGdlbmVzIGZvciB2YWxpZGF0aW9uLgojJyBUaGlzIGZ1bmN0aW9uIGV4cGVjdHMgY29sdW1ucyBgbWFya2VyX2dlbmVfbGFiZWxgIChjZWxsIHR5cGVzKSwgYGVuc2VtYmxfZ2VuZV9pZGAsIGFuZCBgZ2VuZV9zeW1ib2xgCiMnIEBwYXJhbSBzaW5nbGVyX2RmIERhdGEgZnJhbWUgb2Ygc2luZ2xlciBhbm5vdGF0aW9ucyB0byBwbG90LgojJyBUaGlzIGZ1bmN0aW9uIGV4cGVjdHMgY29sdW1ucyBjYWxsZWQgYGxhYmVsX3JlY29kZWRgIChjZWxsIHR5cGVzKSBhbmQgYGNlbGxfaWRgLgojJyBUaGUgYGNlbGxfaWRgIHZhbHVlcyBzaG91bGQgbWF0Y2ggdGhlIGBjZWxsX2lkYCBjb2xEYXRhIGluIHRoZSBtZXJnZWRfc2NlCiMnIEBwYXJhbSB0b3RhbF9jZWxsc19kZiBEYXRhIGZyYW1lIG9mIGNlbGwgY291bnRzIGFuZCBwbG90IG9yZGVyLgojJyBUaGlzIGZ1bmN0aW9uIGV4cGVjdHMgY29sdW1ucyBjYWxsZWQgYGxhYmVsX3JlY29kZWRgIChjZWxsIHR5cGVzKSwgYHlfbGFiZWxgIChjZWxsIHR5cGVzIHdpdGggKHRvdGFsIGNlbGxzKSBhcyBmYWN0b3IgZm9yIHBsb3Qgb3JkZXIpLCBhbmQgYHRvdGFsX2NlbGxzYAojJyBAcGFyYW0gZXhwcmVzc2VkX2dlbmVzIFZlY3RvciBvZiBnZW5lcyB0aGF0IGFyZSBleHByZXNzZWQgaW4gdGhlIG1lcmdlZF9zY2UKIycgQHBhcmFtIGJhcl9vcmRlciBWZWN0b3IgZm9yIHRoZSBhbm5vdGF0aW9uIGJhciBvcmRlcgojJyBAcGFyYW0gbWluX2NlbGxzIE9ubHkgaW5jbHVkZSBnZW5lcyBwcmVzZW50IGluIGF0IGxlYXN0IHRoaXMgbWFueSBjZWxscwojJwojJyBAcmV0dXJucyBEb3RwbG90IG9iamVjdApnZW5lcmF0ZV9kb3RwbG90IDwtIGZ1bmN0aW9uKAogICAgbWVyZ2VkX3NjZSwKICAgIG1hcmtlcnNfZGYsCiAgICBzaW5nbGVyX2RmLAogICAgdG90YWxfY2VsbHNfZGYsCiAgICBleHByZXNzZWRfZ2VuZXMsCiAgICBiYXJfb3JkZXIsCiAgICBtaW5fY2VsbHMgPSAwKSB7CiAgYWxsX21hcmtlcnMgPC0gbWFya2Vyc19kZiB8PgogICAgZHBseXI6OnB1bGwoZW5zZW1ibF9nZW5lX2lkKSB8PgogICAgdW5pcXVlKCkKCiAgIyBjb25zaWRlciBvbmx5IG1hcmtlcnMgdGhhdCBhcmUgZXhwcmVzc2VkCiAgZXhwcmVzc2VkX21hcmtlcnMgPC0gaW50ZXJzZWN0KGFsbF9tYXJrZXJzLCBleHByZXNzZWRfZ2VuZXMpCgogICMgZ2V0IGxvZ2NvdW50cyBmcm9tIG1lcmdlZF9zY2UgZm9yIGV4cHJlc3NlZCBnZW5lcwogIGdlbmVfZXhwX2RmIDwtIHNjdXR0bGU6Om1ha2VQZXJDZWxsREYoCiAgICBtZXJnZWRfc2NlLAogICAgZmVhdHVyZXMgPSBleHByZXNzZWRfbWFya2VycywKICAgIGFzc2F5LnR5cGUgPSAibG9nY291bnRzIiwKICAgIHVzZS5jb2xkYXRhID0gImNlbGxfaWQiLAogICAgdXNlLmRpbXJlZCA9IEZBTFNFCiAgKSB8PgogICAgdGlkeXI6OnBpdm90X2xvbmdlcihzdGFydHNfd2l0aCgiRU5TRyIpLCBuYW1lc190byA9ICJlbnNlbWJsX2dlbmVfaWQiLCB2YWx1ZXNfdG8gPSAibG9nY291bnRzIikgfD4KICAgIGRwbHlyOjptdXRhdGUoZGV0ZWN0ZWQgPSBsb2djb3VudHMgPiAwKQoKICAjIEpvaW4gd2l0aCBjZWxsIHR5cGUgcmVzdWx0cyBhbmQgbWFya2VyIGdlbmUgaW5mbwogIGFsbF9pbmZvX2RmIDwtIHNpbmdsZXJfZGYgfD4KICAgIGRwbHlyOjpsZWZ0X2pvaW4oZ2VuZV9leHBfZGYsIGJ5ID0gImNlbGxfaWQiKSB8PgogICAgIyBhY2NvdW50IGZvciB0aGUgc2FtZSBnZW5lIGJlaW5nIHByZXNlbnQgaW4gbXVsdGlwbGUgY2VsbCB0eXBlcwogICAgZHBseXI6OmxlZnRfam9pbihtYXJrZXJzX2RmLCBieSA9ICJlbnNlbWJsX2dlbmVfaWQiLCByZWxhdGlvbnNoaXAgPSAibWFueS10by1tYW55IikKCiAgIyBkZWZpbmUgeS0gYW5kIHgtIGF4aXMgb3JkZXJzCgogICMgeSBheGlzIC0gc2luZ2xlcgogIHlfbGFiZWxfb3JkZXIgPC0gcmV2KHRvdGFsX2NlbGxzX2RmJHlfbGFiZWwpCgogICMgeCBheGlzIC0gbWFya2VyIGdlbmVzLiB0aGlzIHVzZXMgdGhlIHBhc3NlZCBpbiBiYXJfb3JkZXIKICBtYXJrZXJzX2RmIDwtIG1hcmtlcnNfZGYgfD4KICAgIGRwbHlyOjpmaWx0ZXIoZW5zZW1ibF9nZW5lX2lkICVpbiUgZXhwcmVzc2VkX21hcmtlcnMpIHw+CiAgICBkcGx5cjo6bXV0YXRlKG1hcmtlcl9nZW5lX2xhYmVsID0gZmFjdG9yKG1hcmtlcl9nZW5lX2xhYmVsLCBsZXZlbHMgPSBiYXJfb3JkZXIpKSB8PgogICAgZHBseXI6OmFycmFuZ2UobWFya2VyX2dlbmVfbGFiZWwpCgogIG1hcmtlcl9nZW5lX29yZGVyIDwtIG1hcmtlcnNfZGYgfD4KICAgIGRwbHlyOjpwdWxsKGdlbmVfc3ltYm9sKSB8PgogICAgdW5pcXVlKCkKCiAgZ3JvdXBfc3RhdHNfZGYgPC0gYWxsX2luZm9fZGYgfD4KICAgICMgcmVtb3ZlIGdlbmVzIHRoYXQgYXJlbid0IHByZXNlbnQgaW4gZmluYWwgYW5ub3RhdGlvbnMKICAgIGRwbHlyOjpmaWx0ZXIoZ2VuZV9zeW1ib2wgJWluJSBtYXJrZXJfZ2VuZV9vcmRlcikgfD4KICAgICMgZm9yIGVhY2ggYXNzaWduZWQgY2VsbCB0eXBlL21hcmtlciBnZW5lIGNvbWJvIGdldCB0b3RhbCBkZXRlY3RlZCBhbmQgbWVhbiBleHByZXNzaW9uCiAgICBkcGx5cjo6Z3JvdXBfYnkobGFiZWxfcmVjb2RlZCwgbWFya2VyX2dlbmVfbGFiZWwsIGVuc2VtYmxfZ2VuZV9pZCkgfD4KICAgIGRwbHlyOjpzdW1tYXJpemUoCiAgICAgIGRldGVjdGVkX2NvdW50ID0gc3VtKGRldGVjdGVkKSwKICAgICAgbWVhbl9leHAgPSBtZWFuKGxvZ2NvdW50cykKICAgICkgfD4KICAgIGRwbHlyOjp1bmdyb3VwKCkgfD4KICAgICMgYWRkIHRvdGFsIGNlbGxzCiAgICBkcGx5cjo6bGVmdF9qb2luKHRvdGFsX2NlbGxzX2RmLCBieSA9ICJsYWJlbF9yZWNvZGVkIikgfD4KICAgICMgZ2V0IHRvdGFsIHBlcmNlbnQgZXhwcmVzc2VkCiAgICBkcGx5cjo6bXV0YXRlKHBlcmNlbnRfZXhwID0gKGRldGVjdGVkX2NvdW50IC8gdG90YWxfY2VsbHMpICogMTAwKSB8PgogICAgIyBhZGQgaW4gdmFsaWRhdGlvbiBncm91cCBmb3IgbWFya2VyIGdlbmVzCiAgICAjIHRoaXMgaW5jbHVkZXMgYWxsIHBvc3NpYmxlIG1hcmtlciBnZW5lcyBhbmQgYWxsIHBvc3NpYmxlIHZhbGlkYXRpb24gZ3JvdXAgYXNzaWdubWVudHMKICAgIGRwbHlyOjpsZWZ0X2pvaW4oCiAgICAgIG1hcmtlcnNfZGYsCiAgICAgIGJ5ID0gYygiZW5zZW1ibF9nZW5lX2lkIiwgIm1hcmtlcl9nZW5lX2xhYmVsIiksCiAgICAgIHJlbGF0aW9uc2hpcCA9ICJtYW55LXRvLW1hbnkiCiAgICApCgogIGRvdHBsb3RfZGYgPC0gZ3JvdXBfc3RhdHNfZGYgfD4KICAgIGRwbHlyOjpmaWx0ZXIoCiAgICAgICMgcmVtb3ZlIGxvd2x5IGV4cHJlc3NlZCBtYXJrZXIgZ2VuZXMKICAgICAgbWVhbl9leHAgPiAwLAogICAgICBwZXJjZW50X2V4cCA+IDEwLAogICAgICAjIG9ubHkgc2hvdyBjZWxscyB0aGF0IGhhdmUgYWJvdmUgdGhlIG1pbmltdW0gdG90YWwgbnVtYmVyCiAgICAgIHRvdGFsX2NlbGxzID4gbWluX2NlbGxzCiAgICApIHw+CiAgICAjIHNldCBvcmRlcnMgb2YgZ2VuZSBzeW1ib2wgYW5kIHZhbGlkYXRpb24gZ3JvdXBzCiAgICBkcGx5cjo6bXV0YXRlKAogICAgICB5X2xhYmVsID0gZmFjdG9yKHlfbGFiZWwsIHlfbGFiZWxfb3JkZXIpLAogICAgICBnZW5lX3N5bWJvbCA9IGZhY3RvcihnZW5lX3N5bWJvbCwgbGV2ZWxzID0gbWFya2VyX2dlbmVfb3JkZXIpLAogICAgICBtYXJrZXJfZ2VuZV9sYWJlbCA9IGZhY3RvcihtYXJrZXJfZ2VuZV9sYWJlbCwgbGV2ZWxzID0gYmFyX29yZGVyKQogICAgKQoKCiAgIyMjIE1ha2UgdGhlIGRvdHBsb3QhCiAgZG90cGxvdCA8LSBnZ3Bsb3QoZG90cGxvdF9kZikgKwogICAgYWVzKAogICAgICB5ID0geV9sYWJlbCwKICAgICAgeCA9IGdlbmVfc3ltYm9sLAogICAgICBjb2xvciA9IG1lYW5fZXhwLAogICAgICBzaXplID0gcGVyY2VudF9leHAKICAgICkgKwogICAgZ2VvbV9wb2ludCgpICsKICAgIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYyhvcHRpb24gPSAibWFnbWEiKSArCiAgICBmYWNldF9ncmlkKGNvbHMgPSB2YXJzKG1hcmtlcl9nZW5lX2xhYmVsKSwgc2NhbGVzID0gImZyZWUiLCBzcGFjZSA9ICJmcmVlIikgKwogICAgdGhlbWVfY2xhc3NpYygpICsKICAgIHRoZW1lKAogICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAidHJhbnNwYXJlbnQiLCBjb2xvciA9IE5BKSwKICAgICAgc3RyaXAucGxhY2VtZW50ID0gIm91dHNpZGUiLAogICAgICBzdHJpcC50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSwgdmp1c3QgPSAwLjUpLAogICAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgICAgcGFuZWwuc3BhY2luZyA9IHVuaXQoMC41LCAibGluZXMiKSAjIGFkanVzdCBzcGFjaW5nIGFuZCBtYXRjaCB3aXRoIGFubm90YXRpb24gYmFyCiAgICApICsKICAgIGxhYnMoCiAgICAgIHggPSAiVmFsaWRhdGlvbiBtYXJrZXIgZ2VuZXMiLAogICAgICB5ID0gIlNpbmdsZVIgbGFiZWwiLAogICAgICBjb2xvciA9ICJNZWFuIGdlbmUgZXhwcmVzc2lvbiIsCiAgICAgIHNpemUgPSAiUGVyY2VudCBjZWxscyBleHByZXNzZWQiCiAgICApCgoKICAjIGFkZCBhbm5vdGF0aW9uIGJhciBhbGlnbmluZyBtYXJrZXIgZ2VuZXMgd2l0aCB2YWxpZGF0aW9uIGdyb3VwCiAgY29sb3JfYmFyIDwtIGdncGxvdChkb3RwbG90X2RmLCBhZXMoeCA9IGdlbmVfc3ltYm9sLCB5ID0gMSwgZmlsbCA9IG1hcmtlcl9nZW5lX2xhYmVsKSkgKwogICAgZ2VvbV90aWxlKCkgKwogICAgZmFjZXRfZ3JpZChjb2xzID0gdmFycyhtYXJrZXJfZ2VuZV9sYWJlbCksIHNjYWxlcyA9ICJmcmVlIiwgc3BhY2UgPSAiZnJlZSIpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNlbGx0eXBlX3BhbCwgYnJlYWtzID0gbGV2ZWxzKGRvdHBsb3RfZGYkbWFya2VyX2dlbmVfbGFiZWwpKSArCiAgICBnZ21hcDo6dGhlbWVfbm90aGluZygpICsKICAgIHRoZW1lKAogICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAidHJhbnNwYXJlbnQiLCBjb2xvciA9IE5BKSwKICAgICAgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMCwgdmp1c3QgPSAwLjUsIHNpemUgPSAxMiksCiAgICAgIHN0cmlwLnBsYWNlbWVudCA9ICJvdXRzaWRlIiwKICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICBwYW5lbC5zcGFjaW5nID0gdW5pdCgwLjUsICJsaW5lcyIpLAogICAgICBzdHJpcC5jbGlwID0gIm9mZiIKICAgICkgKwogICAgbGFicyhmaWxsID0gIiIpCgogIGNvbWJpbmVkX3Bsb3QgPC0gY29sb3JfYmFyIC8gZG90cGxvdCArCiAgICBwYXRjaHdvcms6OnBsb3RfbGF5b3V0KG5jb2wgPSAxLCBoZWlnaHRzID0gYygwLjEsIDQpKQoKICByZXR1cm4oY29tYmluZWRfcGxvdCkKfQpgYGAKCiMjIyBQcmVwYXJlIGlucHV0IGRhdGEKClJlYWQgU0NFIG9iamVjdCB0byBnZXQgY29uc2Vuc3VzIGNlbGwgdHlwZXMgYW5kIFVNQVAgY29vcmRpbmF0ZXM6CgpgYGB7cn0KbWVyZ2VkX3NjZSA8LSByZWFkUkRTKHNjZV9maWxlKQojIGltbWVkaWF0ZWx5IHJlbW92ZSBhc3NheXMgd2UgZG9uJ3QgbmVlZCBmb3Igc3BhY2UKYXNzYXkobWVyZ2VkX3NjZSwgInNwbGljZWQiKSA8LSBOVUxMCmFzc2F5KG1lcmdlZF9zY2UsICJjb3VudHMiKSA8LSBOVUxMCgojIFJVSCBST0ghISEKIyBodHRwczovL2dpdGh1Yi5jb20vQWxleHNMZW1vbmFkZS9zY3BjYVRvb2xzL2lzc3Vlcy8yOTgKbWVyZ2VkX3NjZSRjZWxsX2lkIDwtIGNvbG5hbWVzKG1lcmdlZF9zY2UpCmBgYAoKUmVhZCBjZWxsIHR5cGUgZGF0YSBmcmFtZXM6CgpgYGB7cn0Kc2luZ2xlcl9kZiA8LSBzaW5nbGVyX2ZpbGVzIHw+CiAgcHVycnI6Om1hcChyZWFkcjo6cmVhZF90c3YpIHw+CiAgcHVycnI6Omxpc3RfcmJpbmQobmFtZXNfdG8gPSAibGlicmFyeV9pZCIpIHw+CiAgZHBseXI6OnNlbGVjdCgtZGVsdGEubmV4dCwgLWxhYmVscykgfD4KICBkcGx5cjo6bXV0YXRlKAogICAgIyBhZGQgY2VsbCBpZCBjb2x1bW4gZm9yIHVuaXF1ZSByb3dzICYgam9pbmluZwogICAgY2VsbF9pZCA9IGdsdWU6OmdsdWUoIntsaWJyYXJ5X2lkfS17YmFyY29kZXN9IiksCiAgICAjIHJlY29kZSBOQSAtPiAidW5rbm93bl9zaW5nbGVyIgogICAgcHJ1bmVkLmxhYmVscyA9IGlmZWxzZSgKICAgICAgaXMubmEocHJ1bmVkLmxhYmVscyksCiAgICAgICJ1bmtub3duX3NpbmdsZXIiLAogICAgICBwcnVuZWQubGFiZWxzCiAgICApCiAgKQoKIyB2YWxpZGF0aW9uIGRhdGEgZnJhbWVzCnZhbGlkYXRpb25fZGYgPC0gcmVhZHI6OnJlYWRfdHN2KHZhbGlkYXRpb25fdXJsKSB8PgogIGRwbHlyOjpzZWxlY3QoY29uc2Vuc3VzX2Fubm90YXRpb24sIHZhbGlkYXRpb25fZ3JvdXBfYW5ub3RhdGlvbikKY29uc2Vuc3VzX21hcmtlcnNfZGYgPC0gcmVhZHI6OnJlYWRfdHN2KGNvbnNlbnN1c19tYXJrZXJzX2ZpbGUpCm5iYXRsYXNfbWFya2Vyc19kZiA8LSByZWFkcjo6cmVhZF90c3YobmJhdGxhc19tYXJrZXJzX2ZpbGUpCgoKIyBzZXQgdXAgcGFsZXR0ZSBmb3IgdW1hcHMKcGFsZXR0ZV9kZiA8LSByZWFkcjo6cmVhZF90c3YocGFsZXR0ZV9maWxlKQpjZWxsdHlwZV9wYWwgPC0gcGFsZXR0ZV9kZiRoZXhfY29sb3IKbmFtZXMoY2VsbHR5cGVfcGFsKSA8LSBwYWxldHRlX2RmJGhhcm1vbml6ZWRfbGFiZWwKYGBgCgpKb2luIGFuZCBwcmVwYXJlIGRhdGEgZm9yIHVzZToKCmBgYHtyfQpjZWxsdHlwZV9kZiA8LSBzY3V0dGxlOjptYWtlUGVyQ2VsbERGKAogIG1lcmdlZF9zY2UsCiAgdXNlLmNvbGRhdGEgPSBjKCJiYXJjb2RlcyIsICJzYW1wbGVfaWQiLCAibGlicmFyeV9pZCIsICJjb25zZW5zdXNfY2VsbHR5cGVfYW5ub3RhdGlvbiIpLAogIHVzZS5kaW1yZWQgPSBjKCJVTUFQIikKKSB8PgogICMgdGhlcmUgYXJlIHJlcGVhdGVkIGJhcmNvZGVzIHNvIHdlIG5lZWQgdG8ga2VlcCBjZWxsX2lkIGFyb3VuZAogIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKCJjZWxsX2lkIikgfD4KICBkcGx5cjo6cmVuYW1lKAogICAgVU1BUDEgPSBVTUFQLjEsCiAgICBVTUFQMiA9IFVNQVAuMiwKICAgIGNvbnNlbnN1c19hbm5vdGF0aW9uID0gY29uc2Vuc3VzX2NlbGx0eXBlX2Fubm90YXRpb24KICApIHw+CiAgZHBseXI6OmxlZnRfam9pbih2YWxpZGF0aW9uX2RmLCBieSA9ICJjb25zZW5zdXNfYW5ub3RhdGlvbiIpIHw+CiAgIyByZWNvZGUgTkFzIHRvICJ1bmtub3duX2NvbnNlbnN1cyIgYW5kIHJlbW92ZSBmdWxsIGNvbnNlbnN1cyBsYWJlbHMKICBkcGx5cjo6bXV0YXRlKHZhbGlkYXRpb25fZ3JvdXBfYW5ub3RhdGlvbiA9IGlmZWxzZSgKICAgIGlzLm5hKHZhbGlkYXRpb25fZ3JvdXBfYW5ub3RhdGlvbiksCiAgICAidW5rbm93bl9jb25zZW5zdXMiLAogICAgdmFsaWRhdGlvbl9ncm91cF9hbm5vdGF0aW9uCiAgKSkgfD4KICBkcGx5cjo6c2VsZWN0KC1jb25zZW5zdXNfYW5ub3RhdGlvbikgfD4KICBkcGx5cjo6bGVmdF9qb2luKHNpbmdsZXJfZGYsIGJ5ID0gYygiY2VsbF9pZCIsICJiYXJjb2RlcyIsICJsaWJyYXJ5X2lkIikpCgojIFJlY29kZSBOQkF0bGFzIGNlbGwgdHlwZXMgd2hlcmUgcG9zc2libGUgc28gdGhleSBtYXRjaCB3aXRoIGNvbG9ycwpjZWxsdHlwZV9yZWNvZGVkX2RmIDwtIGNlbGx0eXBlX2RmIHw+CiAgIyByZW5hbWUgdG8gbWFrZSBhbm5vdGF0aW9uIHNvdXJjZXMgbW9yZSBjbGVhcgogIGRwbHlyOjpyZW5hbWUoCiAgICAiY29uc2Vuc3VzX3ZhbGlkYXRpb24iID0gdmFsaWRhdGlvbl9ncm91cF9hbm5vdGF0aW9uLAogICAgInNpbmdsZXIiID0gcHJ1bmVkLmxhYmVscwogICkgfD4KICAjIHBpdm90IGxvbmdlciBmb3Igd3JhbmdsaW5nCiAgdGlkeXI6OnBpdm90X2xvbmdlcigKICAgIGMoY29uc2Vuc3VzX3ZhbGlkYXRpb24sIHNpbmdsZXIpLAogICAgbmFtZXNfdG8gPSAiYW5ub3RhdGlvbl90eXBlIiwKICAgIHZhbHVlc190byA9ICJsYWJlbCIKICApIHw+CiAgaGFybW9uaXplX2NlbGx0eXBlcyhsYWJlbCwgbGFiZWxfcmVjb2RlZCkgfD4KICBkcGx5cjo6c2VsZWN0KC1sYWJlbCkKYGBgCgojIyBIZWF0bWFwCgpUaGlzIHNlY3Rpb24gY29tcGFyZXMgU2luZ2xlUiBhbm5vdGF0aW9ucyB0byBjb25zZW5zdXMgY2VsbCB0eXBlIGFubm90YXRpb25zIHVzaW5nIGEgaGVhdG1hcC4KVGhlIGhlYXRtYXAgaXMgY29sb3JlZCBieSBKYWNjYXJkIHNpbWlsYXJpdHkgaW5kZXguCgoKYGBge3IsIGZpZy5oZWlnaHQgPSAxMCwgZmlnLndpZHRoID0gMTB9IAojIFBpdm90IHdpZGVyIGZvciBoZWF0bWFwIGZ1bmN0aW9ucwpjZWxsdHlwZV9yZWNvZGVkX3dpZGVfZGYgPC0gY2VsbHR5cGVfcmVjb2RlZF9kZiB8PgogIHRpZHlyOjpwaXZvdF93aWRlcigKICAgIG5hbWVzX2Zyb20gPSBhbm5vdGF0aW9uX3R5cGUsCiAgICB2YWx1ZXNfZnJvbSA9IGxhYmVsX3JlY29kZWQKICApCgpoZWF0bWFwX2NvbF9mdW4gPC0gY2lyY2xpemU6OmNvbG9yUmFtcDIoYygwLCAxKSwgY29sb3JzID0gYygid2hpdGUiLCAiZGFya3NsYXRlYmx1ZSIpKQoKbWFrZV9qYWNjYXJkX2hlYXRtYXAoCiAgY2VsbHR5cGVfcmVjb2RlZF93aWRlX2RmLAogICJjb25zZW5zdXNfdmFsaWRhdGlvbiIsCiAgInNpbmdsZXIiLAogICJDb25zZW5zdXMgdmFsaWRhdGlvbiBsYWJlbCIsCiAgIlNpbmdsZVIgTkJBdGxhcyBsYWJlbCIKKQpgYGAKCiMjIFVNQVBzCgpUaGlzIHNlY3Rpb24gdmlzdWFsaXplcyBhbmQgY29tcGFyZXMgU2luZ2xlUiBhbm5vdGF0aW9ucyB0byBjb25zZW5zdXMgY2VsbCB0eXBlIGFubm90YXRpb25zIHVzaW5nIFVNQVBzLgpOb3RlIHRoYXQgdGhlIGRpc3BsYXllZCBVTUFQIGlzIGZyb20gYSBtZXJnZWQgb2JqZWN0IHRoYXQgaGFzIF9ub3QgYmVlbiBiYXRjaC1jb3JyZWN0ZWQuXwoKQ2VsbCB0eXBlIGxhYmVscyBoYXZlIGJlZW4gaGFybW9uaXplZCBiZXR3ZWVuIHNvdXJjZXMgd2hlcmV2ZXIgcG9zc2libGUuCk5vdGUgdGhhdCBlYWNoIHNldCBvZiBsYWJlbHMgaGFzIGl0cyBvd24gInN0cm9tYWwiIGNhdGVnb3J5IHdoaWNoIHRoZSBsYWJlbHMgZGlzdGluZ3Vpc2guCkluIGFkZGl0aW9uLCBjZWxscyBsYWJlbGVkIGB1bmtub3duX2NvbnNlbnN1c2AgYXJlIHRob3NlIHdpdGggbm8gYXNzaWduZWQgY29uc2Vuc3VzIGxhYmVsLCBhbmQgY2VsbHMgbGFiZWxlZCBgdW5rbm93bl9zaW5nbGVyYCBhcmUgdGhvc2Ugd2hlcmUgU2luZ2xlUiBjb3VsZCBub3QgY29uZmlkZW50bHkgYXNzaWduIGEgbGFiZWwuCgojIyMgQ29tcGxldGUgVU1BUAoKRmlyc3QsIHdlIGRpc3BsYXkgdGhlIGNvbnNlbnN1cyBhbmQgU2luZ2xlUiBhbm5vdGF0aW9ucyBmb3IgYWxsIGNlbGxzLgoKYGBge3IsIGZpZy53aWR0aCA9IDE0LCBmaWcuaGVpZ2h0ID0gN30KZ2dwbG90KGNlbGx0eXBlX3JlY29kZWRfZGYpICsKICBhZXMoeCA9IFVNQVAxLCB5ID0gVU1BUDIsIGNvbG9yID0gbGFiZWxfcmVjb2RlZCkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDAuMjUsIGFscGhhID0gMC41KSArCiAgc2NhbGVfY29sb3JfbWFudWFsKAogICAgdmFsdWVzID0gY2VsbHR5cGVfcGFsLAogICAgbmFtZSA9ICJIYXJtb25pemVkIGNlbGwgdHlwZXMiCiAgKSArCiAgZmFjZXRfd3JhcCh2YXJzKGFubm90YXRpb25fdHlwZSkpICsKICBjb29yZF9lcXVhbCgpICsKICB0aGVtZSgKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKQogICkgKwogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSAyLCBhbHBoYSA9IDEpKSkKYGBgCgojIyMgU2luZ2xlUiBhbm5vdGF0aW9ucyBvbmx5CgpCZWxvdyB3ZSBkaXNwbGF5IGEgZmFjZXRlZCBVTUFQIHRvIGhpZ2hsaWdodCB0aGUgU2luZ2xlUiBhbm5vdGF0aW9ucy4KTGlnaHQgZ3JheSBjZWxscyBpbiBlYWNoIHBhbmVsIHJlcHJlc2VudCBhbGwgb3RoZXIgY2VsbCB0eXBlcy4KCmBgYHtyIGZpZy53aWR0aCA9IDE2LCBmaWcuaGVpZ2h0ID0gMTZ9CmNlbGx0eXBlX3JlY29kZWRfZGYgfD4KICBkcGx5cjo6ZmlsdGVyKGFubm90YXRpb25fdHlwZSA9PSAic2luZ2xlciIpIHw+CiAgZmFjZXRlZF91bWFwKAogICAgYW5ub3RhdGlvbl9jb2x1bW4gPSBsYWJlbF9yZWNvZGVkLAogICAgY2VsbHR5cGVfY29sb3JzID0gY2VsbHR5cGVfcGFsCiAgKQpgYGAKCgojIyMgRmFjZXRlZCBjb21wYXJpc29uIHRvIGNvbnNlbnN1cyBjZWxsIHR5cGVzCgpCZWxvdywgd2UgZGlzcGxheSBmYWNldGVkIFVNQVBzIGhpZ2hsaWdodGluZyBhIHNpbmdsZSBjZWxsIHR5cGUgYnV0IGNvbnNpZGVyaW5nIG9ubHkgY2VsbCB0eXBlcyB0aGF0IGhhdmUgZGlyZWN0IGNvcnJlc3BvbmRlbmNlIGJldHdlZW4gU2luZ2xlUiBhbmQgY29uc2Vuc3VzIGNlbGwgdHlwZXMuClRoaXMgYWxsb3dzIHVzIHRvIHNlZSBpZiB0aGUgbm9ybWFsIGNlbGxzIHRoYXQgU2luZ2xlUiBpcyBsYWJlbGluZyBjb3JyZXNwb25kIHdlbGwgdG8gdGhlIG5vcm1hbCBjZWxscyBpZGVudGlmaWVkIGJ5IGNvbnNlbnN1cyBsYWJlbHMuCkVhY2ggcm93IGRpc3BsYXlzIGEgY2VsbCB0eXBlIHdoZXJlIHRoZSBsZWZ0IGNvbHVtbiBzaG93cyB0aGUgY29uc2Vuc3VzIHZlcnNpb24gYW5kIHRoZSByaWdodCBjb2x1bW4gc2hvd3MgdGhlIFNpbmdsZVIgdmVyc2lvbi4KCkluIGFkZGl0aW9uLCB3ZSBpbmNsdWRlIGEgY2F0ZWdvcnkgYmVsb3cgYHB1dGF0aXZlLXR1bW9yYCB0byBkaXJlY3RseSBjb21wYXJlIHRoZSB1bmtub3duIGNvbnNlbnN1cyBsYWJlbHMgd2l0aCB0aGUgYE5ldXJvZW5kb2NyaW5lYCBjZWxscyBsYWJlbGVkIGJ5IFNpbmdsZVIuIApXaGlsZSB0aGVzZSBjYXRlZ29yaWVzIGFyZSBub3QgbmVjZXNzYXJpbHkgZGlyZWN0bHkgY29tcGFyYWJsZSwgdGhleSBhcmUgZWFjaCBtb3N0IGxpa2VseSB0byBjb250YWluIHR1bW9yIGNlbGxzLgoKYGBge3IsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTI4fQpkaXJlY3RfY2VsbHR5cGVfbWF0Y2hlcyA8LSBjKAogICJCIGNlbGwiLAogICJUIGNlbGwiLAogICJteWVsb2lkIiwKICAiZmlicm9ibGFzdCIsCiAgImVuZG90aGVsaWFsIGNlbGwiLAogICJwbGFzbWEgY2VsbCIsCiAgIm5hdHVyYWwga2lsbGVyIGNlbGwiLAogICMgd2UnbGwgdXNlIHRoaXMgbGFiZWwgdG8gYmUgYWJsZSB0byBkaXJlY3RseSBjb21wYXJlIHVua25vd25fY29uc2Vuc3VzIHRvIE5ldXJvZW5kb2NyaW5lCiAgInB1dGF0aXZlLXR1bW9yIgopCgoKY2VsbHR5cGVfZmFjZXRfZGYgPC0gY2VsbHR5cGVfcmVjb2RlZF9kZiB8PgogICMgcmVjb2RlIHNvIE5ldXJvZW5kb2NyaW5lIG1hdGNoZXMgd2l0aCB1bmtub3duX2NvbnNlbnN1cyBpbiB0aGUgcGxvdAogIGRwbHlyOjptdXRhdGUoCiAgICBsYWJlbF9yZWNvZGVkID0gaWZlbHNlKAogICAgICBsYWJlbF9yZWNvZGVkICVpbiUgYygiTmV1cm9lbmRvY3JpbmUiLCAidW5rbm93bl9jb25zZW5zdXMiKSwKICAgICAgInB1dGF0aXZlLXR1bW9yIiwKICAgICAgbGFiZWxfcmVjb2RlZAogICAgKQogICkgfD4KICBkcGx5cjo6ZmlsdGVyKGxhYmVsX3JlY29kZWQgJWluJSBkaXJlY3RfY2VsbHR5cGVfbWF0Y2hlcykKCmZhY2V0ZWRfdW1hcCgKICBjZWxsdHlwZV9mYWNldF9kZiwKICBhbm5vdGF0aW9uX2NvbHVtbiA9IGxhYmVsX3JlY29kZWQsCiAgY2VsbHR5cGVfY29sb3JzID0gY2VsbHR5cGVfcGFsLAogIGZhY2V0X3R5cGUgPSAiZ3JpZCIsCiAgYW5ub3RhdGlvbl90eXBlX2NvbHVtbiA9IGFubm90YXRpb25fdHlwZQopCmBgYAoKCgojIyBNYXJrZXIgZ2VuZSBleHByZXNzaW9uIGRvdHBsb3RzCgpJbiB0aGlzIHNlY3Rpb24gd2UnbGwgdmFsaWRhdGUgYW5ub3RhdGlvbnMgdXNpbmcgdHdvIHNldHMgb2YgbWFya2VyIGdlbmVzOgoKKiBNYXJrZXIgZ2VuZXMgaWRlbnRpZmllZCBpbiBOQkF0bGFzIGNvcnJlc3BvbmRpbmcgdG8gdGhlIGF0bGFzIGNlbGwgdHlwZXMKICAqIFRoaXMgd2lsbCB0ZWxsIHVzIGlmIHdlIGFyZSBwaWNraW5nIHVwIGNvbXBhcmFibGUgc2lnbmFsIGluIG91ciBkYXRhIHRoYXQgcGxheXMgd2VsbCB3aXRoIE5CQXRsYXMKKiBDb25zZW5zdXMgY2VsbCB0eXBlIG1hcmtlciBnZW5lcwogICogVGhpcyB3aWxsIHByb3ZpZGUgYW4gaW5kZXBlbmRlbnQgYXNzZXNzbWVudCBvZiB0aGUgcmVsaWFiaWxpdHkgb2YgU2luZ2xlUiBub3JtYWwgY2VsbCB0eXBlIGFzc2lnbm1lbnRzIAoKYGBge3J9CiMgUHJlcGFyZSB0aGUgZXhwcmVzc2VkX2dlbmVzIHZlY3RvcgojIHdlIG9ubHkgY2FyZSBhYm91dCBpZiB0aGF0IGdlbmUgaXMgZXhwcmVzc2VkIG90aGVyd2lzZSB3ZSB3b24ndCB3YXN0ZSBtZW1vcnkgYW5kIGluY2x1ZGUgaXQKZ2VuZV9zdW1zIDwtIHJvd0RhdGEobWVyZ2VkX3NjZSkgfD4KICBhcy5kYXRhLmZyYW1lKCkgfD4KICBkcGx5cjo6c2VsZWN0KGNvbnRhaW5zKCJkZXRlY3RlZCIpKSB8PgogIGFzLm1hdHJpeCgpIHw+CiAgcm93U3VtcygpCmV4cHJlc3NlZF9nZW5lcyA8LSBuYW1lcyhnZW5lX3N1bXMpW2dlbmVfc3VtcyA+IDBdCgojIFByZXBhcmUgZGF0YSBmcmFtZSB3aXRoIHNpbmdsZXIgbGFiZWxzIHRvIHBsb3QKc2luZ2xlcl9yZWNvZGVkX2RmIDwtIGNlbGx0eXBlX3JlY29kZWRfZGYgfD4KICBkcGx5cjo6ZmlsdGVyKAogICAgYW5ub3RhdGlvbl90eXBlID09ICJzaW5nbGVyIiwKICAgICMgd2UgZG9uJ3QgY29uc2lkZXIgdGhlIE5BcyBoZXJlCiAgICBsYWJlbF9yZWNvZGVkICE9ICJ1bmtub3duX3NpbmdsZXIiCiAgKSB8PgogIGRwbHlyOjpzZWxlY3QoY2VsbF9pZCwgbGFiZWxfcmVjb2RlZCkKCgojIGdldCB0b3RhbCBudW1iZXIgb2YgY2VsbHMgcGVyIGZpbmFsIGFubm90YXRpb24gZ3JvdXAgYW5kIHNldCB1cCB5X2xhYmVsCnRvdGFsX2NlbGxzX2RmIDwtIHNpbmdsZXJfcmVjb2RlZF9kZiB8PgogIGRwbHlyOjpjb3VudChsYWJlbF9yZWNvZGVkLCBuYW1lID0gInRvdGFsX2NlbGxzIikgfD4KICBkcGx5cjo6YXJyYW5nZShkZXNjKHRvdGFsX2NlbGxzKSkgfD4KICBkcGx5cjo6bXV0YXRlKHlfbGFiZWwgPSBnbHVlOjpnbHVlKCJ7bGFiZWxfcmVjb2RlZH0gKHt0b3RhbF9jZWxsc30pIikpCgpzaW5nbGVyX29yZGVyIDwtIHRvdGFsX2NlbGxzX2RmJHlfbGFiZWwKdG90YWxfY2VsbHNfZGYkeV9sYWJlbCA8LSBmYWN0b3IodG90YWxfY2VsbHNfZGYkeV9sYWJlbCwgbGV2ZWxzID0gc2luZ2xlcl9vcmRlcikKYGBgCgoKCiMjIyBOQkF0bGFzIG1hcmtlciBnZW5lcwoKV2UnbGwgc2hvdyB0aGUgdG9wLXNldmVuIGhpZ2hlc3QgbG9nRkMgbWFya2VyIGdlbmVzIHBlciBjZWxsIHR5cGUgZm9yIHZhbGlkYXRpb24uCgpgYGB7cn0KIyBudW1iZXIgb2YgbWFya2VyIGdlbmVzIHRvIGNvbnNpZGVyIHBlciB2YWxpZGF0aW9uIGdyb3VwCm5fbWFya2VyX2dlbmVzIDwtIDcKCm5iYXRsYXNfbWFya2Vyc19kZiA8LSBuYmF0bGFzX21hcmtlcnNfZGYgfD4KICAjIGtlZXAgb25seSB0aGUgdG9wIGBuX21hcmtlcl9nZW5lc2AKICBkcGx5cjo6ZmlsdGVyKGRpcmVjdGlvbiA9PSAidXAiKSB8PgogIGRwbHlyOjpncm91cF9ieShOQkF0bGFzX2xhYmVsKSB8PgogIGRwbHlyOjptdXRhdGUocmFua19sZmMgPSByYW5rKC1hdmdfbG9nMkZDKSkgfD4KICBkcGx5cjo6dW5ncm91cCgpIHw+CiAgZHBseXI6OmZpbHRlcihyYW5rX2xmYyA8PSBuX21hcmtlcl9nZW5lcykgfD4KICBoYXJtb25pemVfY2VsbHR5cGVzKE5CQXRsYXNfbGFiZWwsIG1hcmtlcl9nZW5lX2xhYmVsKSB8PgogIGRwbHlyOjpzZWxlY3QobWFya2VyX2dlbmVfbGFiZWwsIGdlbmVfc3ltYm9sLCBlbnNlbWJsX2dlbmVfaWQpCgpuYmF0bGFzX2Jhcl9vcmRlciA8LSB0b3RhbF9jZWxsc19kZiRsYWJlbF9yZWNvZGVkCmBgYAoKCmBgYHtyLCBmaWcud2lkdGggPSAyNCwgZmlnLmhlaWdodCA9IDEyfQpnZW5lcmF0ZV9kb3RwbG90KAogIG1lcmdlZF9zY2UsCiAgbmJhdGxhc19tYXJrZXJzX2RmLAogIHNpbmdsZXJfcmVjb2RlZF9kZiwKICB0b3RhbF9jZWxsc19kZiwKICBleHByZXNzZWRfZ2VuZXMsCiAgbmJhdGxhc19iYXJfb3JkZXIKKQpgYGAKCgoKIyMjIENvbnNlbnN1cyB2YWxpZGF0aW9uIG1hcmtlciBnZW5lcwoKCmBgYHtyfQojIHByZXBhcmUgZm9yIGRvdHBsb3QKCmNvbnNlbnN1c19tYXJrZXJzX2RmIDwtIGNvbnNlbnN1c19tYXJrZXJzX2RmIHw+CiAgIyB3ZSdsbCB1c2UgdGhpcyB0byBoZWxwIGRldGVybWluZSB0aGUgYmFyIG9yZGVyCiAgaGFybW9uaXplX2NlbGx0eXBlcyhOQkF0bGFzX2xhYmVsLCBOQkF0bGFzX2xhYmVsX3JlY29kZWQpIHw+CiAgZHBseXI6OnNlbGVjdCgKICAgIG1hcmtlcl9nZW5lX2xhYmVsID0gdmFsaWRhdGlvbl9ncm91cF9hbm5vdGF0aW9uLAogICAgZW5zZW1ibF9nZW5lX2lkLAogICAgZ2VuZV9zeW1ib2wsCiAgICBOQkF0bGFzX2xhYmVsX3JlY29kZWQKICApCgojIGdldCB0aGUgYmFyIG9yZGVyCmNvbnNlbnN1c19iYXJfb3JkZXIgPC0gdG90YWxfY2VsbHNfZGYgfD4KICBkcGx5cjo6c2VsZWN0KGxhYmVsX3JlY29kZWQsIHlfbGFiZWwpIHw+CiAgIyBpbm5lciEhISEKICBkcGx5cjo6aW5uZXJfam9pbihjb25zZW5zdXNfbWFya2Vyc19kZiwgYnkgPSBjKCJsYWJlbF9yZWNvZGVkIiA9ICJOQkF0bGFzX2xhYmVsX3JlY29kZWQiKSkgfD4KICBkcGx5cjo6YXJyYW5nZSh5X2xhYmVsKSB8PgogIGRwbHlyOjpwdWxsKG1hcmtlcl9nZW5lX2xhYmVsKSB8PgogIHVuaXF1ZSgpCmBgYAoKCmBgYHtyLCBmaWcud2lkdGggPSAyMiwgZmlnLmhlaWdodCA9IDEyfQpnZW5lcmF0ZV9kb3RwbG90KAogIG1lcmdlZF9zY2UsCiAgY29uc2Vuc3VzX21hcmtlcnNfZGYsCiAgc2luZ2xlcl9yZWNvZGVkX2RmLAogIHRvdGFsX2NlbGxzX2RmLAogIGV4cHJlc3NlZF9nZW5lcywKICBjb25zZW5zdXNfYmFyX29yZGVyCikKYGBgCgojIyBTZXNzaW9uIEluZm8KCmBgYHtyIHNlc3Npb24gaW5mb30KIyByZWNvcmQgdGhlIHZlcnNpb25zIG9mIHRoZSBwYWNrYWdlcyB1c2VkIGluIHRoaXMgYW5hbHlzaXMgYW5kIG90aGVyIGVudmlyb25tZW50IGluZm9ybWF0aW9uCnNlc3Npb25JbmZvKCkKYGBgCg==